package com.log.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import com.log.config.RabbitMQConfig;
import com.log.enums.EventMessageType;
import com.log.feign.SyncFeignService;
import com.log.model.EventMessage;
import com.log.model.LogDTO;
import com.log.model.feign.LogInfoDTO;
import com.log.request.AddLogRequest;
import com.log.service.LogService;
import com.log.util.CommonUtil;
import com.log.util.IDUtil;
import com.log.util.JsonData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.beans.IntrospectionException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassDescription:
 * @Author: zhaoyi
 * @Created: 2024/7/12 11:12
 */

@Service
@Slf4j
@RequiredArgsConstructor
public class LogServiceImpl implements LogService {


    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    private final SyncFeignService syncFeignService;

//    private static final String PATH = "C:\\Users\\MissZhou\\Desktop\\testFiles\\";
    private static final String PATH = "C:\\Users\\hp\\Desktop\\files\\";

    AtomicInteger logNumCount = new AtomicInteger(0);


    /**
     *
     * {"id":1,"tag":"A"}
     * A.log ，B.log
     * 1.1 提供接口可以自由创建多线程平摊n次调用日志输出（例如5个线程 平摊100次，每个线程要调用20次）;
     * 1.2 每次调用,打印出json格式{"id":1,"tag":"A"} 同时打印两条id相同 tag随机字符（A 或B）的日志分别存到  A.log ，B.log 文日志件;
     * 1.3 每次打印的id要自增唯一;
     * 1.4 每条日志格式样例为：2022-07-01 15:33:07.761 [pool-2-thread-6] INFO  - {"id":977,"tag":"A"};
     * @param request
     * @return
     */
    @Override
    public List<LogDTO> creatLogFile(AddLogRequest request) {

        try{
            ExecutorService threadPool = Executors.newFixedThreadPool(request.getCoreSize());
            if (request.getCoreSize() == null || request.getLogNum() == null) {
                return null;
            }
            // 开始时间（ms）
            long l = System.currentTimeMillis();
            Path aLogFile = Paths.get(PATH+"A.log");
            Path bLogFile = Paths.get(PATH+"B.log");
            // 如果文件不存在，则创建新文件
            if (!Files.exists(aLogFile)) {
                Files.createFile(aLogFile);
            }
            if (!Files.exists(bLogFile)) {
                Files.createFile(bLogFile);
            }
            CountDownLatch countDownLatch = new CountDownLatch(request.getCoreSize());
            AtomicInteger count = new AtomicInteger(0);
            List<Integer> idList = initIdList(request, logNumCount);
            //每个线程打印的日志数量
            int num = request.getLogNum() / request.getCoreSize();
            List<LogDTO> logDTOList = new ArrayList<>();
            Integer coreSize = request.getCoreSize();

//            FileOutputStream aOs = new FileOutputStream(PATH+"A.log");
//            FileOutputStream bOs = new FileOutputStream(PATH+"B.log");
//            FileChannel aChannel = aOs.getChannel();
//            FileChannel bChannel = bOs.getChannel();
//            ByteBuffer buffer = ByteBuffer.allocate(1024*1024);
            for (int i = 0; i < coreSize; i++) {
                List<Integer> subData = null;
                if (i == (coreSize - 1)) {
                    subData = idList.subList(i * num, idList.size());
                } else {
                    subData = idList.subList(i * num, (i + 1) * num);
                }
                List<Integer> finalSubData = subData;
                threadPool.execute(()->{
                    for (Integer id : finalSubData) {
                        String tag = CommonUtil.getStringNumRandom(1);
                        LogDTO logDTO = new LogDTO();
                        logDTO.setId(id);
                        logDTO.setTag(tag);
                        //随机生成两条日志并写入
                        boolean isA = false;
                        String randomTag = CommonUtil.getStringNumRandom(1);
                        if (StringUtils.equals(randomTag, aLogFile.getFileName().toString().substring(0,aLogFile.getFileName().toString().indexOf(".")))) {
                            isA = true;
                        }
                        for (int j = 0; j <2; j++) {
                            if (isA) {
                                addFile(id,aLogFile);
//                                addFileWishChannel(id,buffer,aChannel);
                                isA = false;
                            }else {
                                addFile(id,bLogFile);
//                                addFileWishChannel(id,buffer,bChannel);
                                isA = true;
                            }
                        }
                        logDTOList.add(logDTO);
                        String content = getContent(logDTO);
                        log.info(content);
                        count.incrementAndGet();
                    }
                    countDownLatch.countDown();
                });
            }
            //阻塞主线程
            countDownLatch.await();
//            //关闭流
//            aOs.close();
//            bOs.close();
//            //关闭通道
//            aChannel.close();
//            bChannel.close();
            // 关闭线程池
            threadPool.shutdown();
            // 执行结束时间
            long end_l = System.currentTimeMillis();
            log.info("总耗时:{}s", (end_l - l)/1000);
            log.info("打印数量:{}",count.get());
            if (!CollectionUtils.isEmpty(logDTOList)) {
                return logDTOList;
            }
        }catch (Exception e){
            log.error("创建文件异常：{}",e.getMessage());
        }
        return null;
    }

    private List<Integer> initIdList(AddLogRequest request, AtomicInteger logNumCount) {
        List<Integer> idList = new ArrayList<>();
        for (int i = 0; i < request.getLogNum(); i++) {
            idList.add(logNumCount.incrementAndGet());
        }
        return idList;
    }

    private void addFileWishChannel(Integer pkid ,ByteBuffer buffer, FileChannel channel){

        try {
            LogDTO logDTO = new LogDTO();
            logDTO.setId(pkid);
            logDTO.setTag(CommonUtil.getStringNumRandom(1));
            String content = getContent(logDTO);
            buffer.clear();
            //拼接内容 todo
            buffer.put((content+"\n").getBytes());
            buffer.flip();
            channel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 1.4 每条日志格式样例为：2022-07-01 15:33:07.761 [pool-2-thread-6] INFO  - {"id":977,"tag":"A"};
     */
    private void addFile(Integer pkid , Path path) {
        try {

            LogDTO logDTO = new LogDTO();
            logDTO.setId(pkid);
            logDTO.setTag(CommonUtil.getStringNumRandom(1));
            // 内容写入到文件
            String content = getContent(logDTO);
            Files.write(path,(content + "\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
        } catch (IOException e) {
            log.error("写入文件失败", e.getMessage());
        }

//        try {
//            LogDTO logDTO = new LogDTO();
//            logDTO.setId(pkid);
//            logDTO.setTag(tag);
//            Path filePath = Paths.get(PATH + logDTO.getTag()+".log");
//            // 如果文件不存在，则创建新文件
//            if (!Files.exists(filePath)) {
//                Files.createFile(filePath);
//            }
//            // 内容写入到文件
//            String content = getContent(logDTO);
//            Files.write(path,(content + "\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
//            //异步读写-待完成
//            List<String> lines = Files.readAllLines(path);
//            if (!CollectionUtils.isEmpty(lines)) {
//                Files.write(path, (content + "\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
//            } else {
//                Files.write(path, (content + "\n").getBytes(StandardCharsets.UTF_8));
//            }
//        } catch (Exception e) {
//            log.error("写入文件失败", e);
//        }
    }


    private static String getContent(LogDTO logDTO) {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        return date + " ["+Thread.currentThread().getName()+ "]" + " INFO -" + JSONObject.toJSONString(logDTO);
    }

    /**
     * 2.1 提供接口可以自由创建多线程 读取 功能一 创建的全部日志;
     * 2.2 读取A.log ，B.log的全部日志并对A B日志的内容按相同id进行比较tag值是否相等 并将结果http请求发送给服务B提供的接口;
     */
    @Override
    public JsonData readFileAndSend(Integer poolSize) {
        ExecutorService pool = getExecutorService(poolSize);
        AtomicInteger count = new AtomicInteger(0);
        AtomicInteger pushCount = new AtomicInteger(0);
        CountDownLatch countDownLatch = new CountDownLatch(poolSize);
        //可优化
        List<String> allLineList = getAllLineList();
        if (CollectionUtils.isEmpty(allLineList)) {
            return JsonData.buildError("The file's content is null");
        }
        // 开始时间（ms）
        long l = System.currentTimeMillis();
        List<LogInfoDTO> logInfoDTOList = analysisLines(allLineList,count,pushCount);
        //每个线程处理数据
//        List<List<String>> partition = Lists.partition(allLineList, poolSize.intValue());
        int size = logInfoDTOList.size() / poolSize;
        for (int i = 0; i < poolSize; i++) {
            // 每一个线程任务数据list
            List<LogInfoDTO> subData = null;
            if (i == (poolSize - 1)) {
                subData = logInfoDTOList.subList(i * size, logInfoDTOList.size());
            } else {
                subData = logInfoDTOList.subList(i * size, (i + 1) * size);
            }
            List<LogInfoDTO> finalSubData = subData;
            pool.execute(()->{
                try {
//                    List<LogInfoDTO> logInfoDTOList = analysisLines(finalSubData,count,pushCount);
                    //发送数据B服务改mq异步发送
//                    syncFeignService.syncAndInsertLog(logInfoDTOList);
                    for (LogInfoDTO logInfoDTO : finalSubData) {
                        sendMessage(logInfoDTO);
                    }
                } catch (Exception e) {
                    log.error("线程：{} 执行异常：{}",Thread.currentThread().getId(),e.getMessage());
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
            });
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 关闭线程池
        pool.shutdownNow();
        // 执行结束时间
        long end_l = System.currentTimeMillis();
        log.info("总耗时:{} ms", (end_l - l));
        log.info("处理行数:{}", count.get());
        log.info("推送行数:{}", pushCount.get());
        return JsonData.buildSuccess();
    }

    private List<String> getAllLineList() {
        List<Path> pathList = new ArrayList<>();
        //获取文件下所有文件
        try(Stream<Path> pathStream = Files.walk(Paths.get(PATH))){
            pathList = pathStream.filter(Files::isRegularFile).collect(Collectors.toList());
        }catch (Exception e){
            log.error("get files failed");
        }
        if (CollectionUtils.isEmpty(pathList)) {
            return null;
        }
        //读取所有文件所有内容
        List<String> allLineList = Lists.newArrayList();
        for (Path path : pathList) {
            try {
                List<String> lines = Files.readAllLines(path);
                allLineList.addAll(lines);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (CollectionUtils.isEmpty(allLineList)) {
            return null;
        }
        return allLineList;
    }

    /**
     * 2.2 读取A.log ，B.log的全部日志并对A B日志的内容按相同id进行比较tag值是否相等 并将结果http请求发送给服务B提供的接口;
     * @param lines
     */
    private List<LogInfoDTO> analysisLines(List<String> lines,AtomicInteger count,AtomicInteger pushCount) {

        List<LogInfoDTO> logInfoDTOList = new ArrayList<>();
        //转换logInfo
        lines.stream().forEach(line->{
            String jsonStr = line.substring(line.lastIndexOf("-")+1);
            LogInfoDTO logInfoDTO = JSON.parseObject(jsonStr, LogInfoDTO.class);
            logInfoDTO.setLogText(line);
            logInfoDTOList.add(logInfoDTO);
            count.incrementAndGet();
        });
        //筛选id、tag相同的内容
        Map<String,Object>map = new HashMap<>();
        List<LogInfoDTO> result = new ArrayList<>();
        logInfoDTOList.forEach(logInfoDTO -> {
            String key = logInfoDTO.getId()+ logInfoDTO.getTag();
            if (Objects.isNull(map.get(key))) {
                map.put(key, logInfoDTO.getLogText());
            }else {
                result.add(logInfoDTO);
                pushCount.incrementAndGet();
            }
        });
        result.sort(Comparator.comparing(LogInfoDTO::getId));
        return result;
    }

    private void sendMessage(LogInfoDTO logInfoDTO) {

        EventMessage message = EventMessage.builder()
                .content(JSONObject.toJSONString(logInfoDTO))
                .messageId(IDUtil.geneSnowFlakeID().toString())
                .eventMessageType(EventMessageType.LOG_OPS_CONTENT.name())
                .build();

        rabbitTemplate.convertAndSend(rabbitMQConfig.getLogEventExchange(),rabbitMQConfig.getLogRoutingKey(),message);

    }


//    private void sendMessage(List<LogInfoDTO> logInfoDTOList) {
//
//        EventMessage message = EventMessage.builder()
//                .content(JSONObject.toJSONString(logInfoDTOList))
//                .messageId(IDUtil.geneSnowFlakeID().toString())
//                .eventMessageType(EventMessageType.LOG_OPS_CONTENT.name())
//                .build();
//
//        rabbitTemplate.convertAndSend(rabbitMQConfig.getLogEventExchange(),rabbitMQConfig.getLogRoutingKey(),message);
//
//    }

    private ExecutorService getExecutorService(int poolSize){

//        ExecutorService executorService = Executors.newFixedThreadPool(num);
        return  new ThreadPoolExecutor(
                poolSize,
                poolSize,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue());
    }



}
